﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web.Mvc;

using AMC.SubstitutionCache.Events;
using AMC.SubstitutionCache.Filters;
using AMC.SubstitutionCache.Services;

using Orchard;
using Orchard.DisplayManagement;
using Orchard.Logging;

namespace AMC.SubstitutionCache.Rendering
{

    /// <summary>
    /// Responsible for re-rendering a shape based on the provided SubstitutionContext 
    /// </summary>
    public class SubstitutionShapeRenderer : ISubstitutionShapeRenderer
    {
        private readonly IEnumerable<ISubstitutionEvents> _handlers;
        private readonly IDisplayHelperFactory _displayHelperFactory;
        private readonly IWorkContextAccessor _workContextAccessor;

        public SubstitutionShapeRenderer(
            IEnumerable<ISubstitutionEvents> handlers,
            IDisplayHelperFactory displayHelperFactory,
            IWorkContextAccessor workContextAccessor)
        {
            _handlers = handlers;
            _displayHelperFactory = displayHelperFactory;
            _workContextAccessor = workContextAccessor;

            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        /// <summary>
        /// Re-renders a shape based on the provided SubstitutionContext 
        /// </summary>
        public string GetOutput(SubstitutionContext context, ControllerContext controllerContext)
        {
            var creatingContext = new SubstitutionShapeCreatingContext
            {
                SubstitutionContext = context
            };

            foreach (var handler in _handlers)
                handler.ShapeCreating(creatingContext);

            var shape = creatingContext.Shape;
            context = creatingContext.SubstitutionContext;

            if (shape == null)
            {
                Logger.Debug("Shape is null. Can't render. Returning string.Empty.");
                return string.Empty;
            }

            var createdContext = new SubstitutionShapeCreatedContext
            {
                SubstitutionContext = context,
                Shape = shape
            };

            foreach (var handler in _handlers)
                handler.ShapeCreated(createdContext);

            shape = createdContext.Shape;
            context = createdContext.SubstitutionContext;

            var renderingContext = new SubstitutionRenderingContext
            {
                SubstitutionContext = context,
                Shape = shape
            };

            foreach (var handler in _handlers)
                handler.Rendering(renderingContext);

            if (!string.IsNullOrWhiteSpace(renderingContext.Output))
                return renderingContext.Output;

            shape = renderingContext.Shape;
            context = renderingContext.SubstitutionContext;

            using (var writer = new StringWriter())
            {
                var viewContext = CreateViewContext(controllerContext, shape, writer);

                var displayHelper = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer { ViewData = viewContext.ViewData });
                var output = displayHelper.ShapeExecute(shape);

                output = output != null ? output.ToString() : string.Empty;

                if (_workContextAccessor.GetContext().HttpContext.IsCacheDebugMode())
                    output += "\r\n<!-- Shape '" + context.ShapeType + "' rendered on " + DateTime.UtcNow + " (UTC) -->";

                var renderedContext = new SubstitutionRenderedContext
                {
                    SubstitutionContext = context,
                    Shape = shape,
                    Output = output
                };

                foreach (var handler in _handlers)
                    handler.Rendered(renderedContext);

                return renderedContext.Output;
            } 
        }

        private static ViewContext CreateViewContext(ControllerContext context, dynamic shape, TextWriter writer)
        {
            var view = new ReplacementView();
            var viewDict = new ViewDataDictionary(shape.Model ?? shape);
            var tempDict = new TempDataDictionary();
            var viewContext = new ViewContext(context, view, viewDict, tempDict, writer);

            return viewContext;
        }

        public class ReplacementView : IView
        {
            public void Render(ViewContext viewContext, TextWriter writer)
            {
            }
        }
    }
}